/** Part of Zombie Navigator
	Copyright © 2016  Zombie Navigator Developers
	
	@file
	@module
	@license AGPL-3.0+
	
*/ 'use strict';


/* Addon SDK dependencies */
const _sdkURL = require('sdk/url');
/* */

const _privSchemes = ['resource:', 'chrome:', 'about:'];

/**
	The standard URI parser
	@private
*/
const parse = uriRef => {
	const [, scheme, authority, path, query, fragment] = (uriRef + '')
	.match(new RegExp('^([^:/?#]+:)?(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$'));
	return {
		scheme: scheme
		, authority: authority
		, path: path
		, query: query
		, fragment: fragment
	};
};

/** @private */
const compose = ({scheme, authority, path, query, fragment}) => {
	return [
		scheme || ''
		, authority || ''
		, path
		, query || ''
		, fragment || ''
	].join('');
};


/* Internal APIs */

/**
	@public
	@method
	@param {string} path
	@returns {string}
*/
const filenameToURI = path => {
	const sdkURL = _sdkURL;
	
	path = path + '';
	
	try {
		return sdkURL.fromFilename(path) + '';
	} catch (e) {
		throw new Error(e + '');
	}
};

/**
	Strictly resolves a URI (or IRI) reference, the standard way
	@public
	@method
	@param {string} base
	@param {string} ref
	@returns {string}
*/
const resolve = (base, ref) => {
	const removeDotSegments = path => {
		const input = path.split('/');
		const output = [];
		
		while (input.length) {
			if ('.' === input[0] || '..' === input[0]) {
				input.shift();
				continue;
			}
			
			if ('' === input[0]) {
				if (2 > input.length) {
					break;
				}
				
				if ('.' === input[1]) {
					input.shift();
					input[0] = '';
					if (2 > input.length) {
						input.push('');
					}
					continue;
				}
				
				if ('..' == input[1]) {
					input.shift();
					input[0] = '';
					if (2 > input.length) {
						input.push('');
					}
					output.pop();
					continue;
				}
				
				input.shift();
				if (!output.length) {
					output.push('');
				}
			}
			
			output.push(input.shift());
			input.unshift('');
		}
		
		return output.join('/');
	};
	
	const mergePaths = (basePath, refPath) => {
		const pos = basePath.lastIndexOf('/');
		if (0 > pos) {
			return refPath;
		}
		
		return [basePath.slice(0, 1 + pos), refPath].join('');
	};
	
	
	[base, ref] = [base, ref].map(parse);
	
	const resolved = {};
	if ('undefined' != typeof ref.scheme) {
		resolved.scheme = ref.scheme;
		resolved.authority = ref.authority;
		resolved.path = ref.path;
		resolved.query = ref.query;
	} else {
		if ('undefined' != typeof ref.authority) {
			resolved.authority = ref.authority;
			resolved.path = removeDotSegments(ref.path);
			resolved.query = ref.query;
		} else {
			if ('' === ref.path) {
				resolved.path = base.path;
				if ('undefined' != typeof ref.query) {
					resolved.query = ref.query;
				} else {
					resolved.query = base.query;
				}
			} else {
				if ('/' === ref.path[0]) {
					// absolute path
					resolved.path = removeDotSegments(ref.path);
				} else {
					// relative path
					resolved.path = removeDotSegments(mergePaths(base.path, ref.path));
				}
				
				resolved.query = ref.query;
			}
			
			resolved.authority = base.authority;
		}
		
		resolved.scheme = base.scheme;
	}
	
	resolved.fragment = ref.fragment;
	
	return compose(resolved);
};

const create = uri => {
	const parts = parse(uri);
	return Object.freeze({
		__proto__: null
		,get isPrivileged () {
			return -1 != _privSchemes.indexOf(parts.scheme);
		}
		,get isRemote () {
			// data: neutral, depends on the parent!
			if ('data:' === parts.scheme) return void 0;
			if ('file:' === parts.scheme) return false;
			return !this.isPrivileged;
			// 'remote': ftp://, javascript:, view-source:, etc.
		}
		,get isQualified () {
			return !!parts.scheme;
		}
		,toString () {
			return compose(parts);
		}
	});
};

/**
	@public
	@method
	@param {string} uri
	@returns {bool}
*/
const isPrivileged = uri => create(uri).isPrivileged;

/**
	@public
	@method
	@param {string} uri
	@returns {bool} or {undefined}
*/
const isRemote = uri => create(uri).isRemote;

/**
	@public
	@method
	@param {string} uri
	@returns {bool}
*/
const isQualified = uri => create(uri).isQualified;


/* Exports */
exports.filenameToURI = filenameToURI;
exports.resolve = resolve;
exports.create = create;
exports.isPrivileged = isPrivileged;
exports.isRemote = isRemote;
exports.isQualified = isQualified;


// vim: ts=4 noet ai

